package com.google.appengine.codelab;

import java.io.IOException;
import java.io.PrintWriter;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.HashMap;
import java.util.Map;
//import java.util.List;

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
//import com.google.appengine.api.datastore.Key;

//import com.google.appengine.api.datastore.FetchOptions;
//import com.google.appengine.api.datastore.Query;
//import com.google.appengine.api.datastore.Query.CompositeFilterOperator;
//import com.google.appengine.api.datastore.Query.Filter;
//import com.google.appengine.api.datastore.Query.FilterOperator;
//import com.google.appengine.api.datastore.Query.FilterPredicate;

@SuppressWarnings("serial")
public class UserStory14Servlet extends HttpServlet 
{
        private static final Logger logger = Logger.getLogger ( UserStory14Servlet.class.getCanonicalName ());
                
        protected void doGet ( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException 
        {
        	//MassaDados.load () ;
        	
        	resp.setContentType ( "text/plain" ) ;

        	String byCity    = req.getParameter ( "city" ) ;
            String byState   = req.getParameter ( "state" ) ;
            String byCountry = req.getParameter ( "country" ) ;
                
            logger.log ( Level.INFO, "byCity...: " + byCity ) ;
            logger.log ( Level.INFO, "byState..: " + byState ) ;
            logger.log ( Level.INFO, "byCountry: " + byCountry ) ;
                
            String szIni = req.getParameter ( "dtIni" ) ;
            String szFin = req.getParameter ( "dtFin" ) ;
            
            logger.log ( Level.INFO, "szIni: " + szIni ) ;
            logger.log ( Level.INFO, "szFin: " + szFin ) ;
            
            PrintWriter out = resp.getWriter () ;

            out.println ( consulta ( byCity, byState, byCountry, szIni, szFin )) ;
        }

        private String consulta ( String byCity, String byState, String byCountry, String szIni, String szFin ) 
        {
        	if ( szIni == null || szIni.equals ( "" )) 
            {
        		szIni = "01/01/1970" ;
            }
        	
        	if ( szFin == null || szFin.equals ( "" )) 
            {
        		szFin = "31/12/2100" ;
            }

        	Date dtIni = Common.cDate ( szIni ) ;
            Date dtFin = Common.cDate ( szFin ) ;
      
            logger.log ( Level.INFO, "dtIni: " + dtIni ) ;
            logger.log ( Level.INFO, "dtFin: " + dtFin ) ;

            if ( byCountry == null || byCountry.equals ( "" )) 
            {
                if ( byState == null || byState.equals ( "" )) 
                {  
                	if ( byCity == null || byCity.equals ( "" )) 
                    { 
                	    String msgErro = "Invalid search ! No city (" + byCity + "), no State (" + byState + "), no Country (" + byCountry + ")." ;
                	    	    	    
                	    logger.log ( Level.INFO, msgErro ) ;
                	    	    	    
                	    return ( msgErro );
                    }
                	else
                	{
                	    Iterable<String> list = this.getFraudesByCity ( byCity, dtIni, dtFin ) ;
                        return ( writeJSON ( list )) ;
                	}
                }
                else
                {
                	Iterable<String> list = this.getFraudesByState ( byState, dtIni, dtFin ) ;
                    return ( writeJSON ( list )) ;
                }
            } 
            else 
            {
                Iterable<String> list = this.getFraudesByCountry ( byCountry, dtIni, dtFin ) ;
                return ( writeJSON ( list )) ;
            }	
        }

        public Iterable<String> getFraudesByCountry ( String byCountry, Date dtIni, Date dtFin ) 
        {
        	int totFraudes = 0 ;
        	
        	Map<String, Integer> listCity = new HashMap<String, Integer> () ;
        	Map<String, Integer> listType = new HashMap<String, Integer> () ;
        	
        	ArrayList<String> list = new ArrayList<String> () ;
        	
        	Iterable<Entity> entitiesFraude = Util.listEntities ( "Fraude", null, null ) ;

            for ( Entity entityF : entitiesFraude )
            {
            	Date dataFraude = Common.cDate ( entityF.getProperty ( "fraude_data_deteccao" ).toString ()) ;
 
                if ( dataFraude.after ( dtIni ) && dataFraude.before ( dtFin )) 
                {
                    String transId = entityF.getProperty ( "trans_id" ).toString () ;
                            
                    Iterable<Entity> entitiesTransacao = Util.listEntities ( "Transacao", "trans_id", transId ) ;
                            
                    for ( Entity entityT : entitiesTransacao ) 
                    {
                    	String localidadeId = entityT.getProperty ( "localidade_id" ).toString () ;
                   	
                    	Entity localidade = Localidade.getSingleLocalidade ( localidadeId ) ;
                    	
                    	String szCountry = localidade.getProperty ( "localidade_country" ).toString () ;
                    	
                    	if ( byCountry.equals ( szCountry ))
                    	{
                            String szType = entityF.getProperty ( "fraude_type" ).toString () ;
                    		
                    		if ( listType.containsKey ( szType ))
                                listType.put ( szType, listType.get ( szType ) + 1 );
                            else
                                listType.put ( szType, 1 );
                    		
                    		totFraudes += 1 ;
                    		
                    		String szCity = localidade.getProperty ( "localidade_city" ).toString () ;
                    		
                    		if ( listCity.containsKey ( szCity ))
                                listCity.put ( szCity, listCity.get ( szCity ) + 1 );
                            else
                                listCity.put ( szCity, 1 );
                    		                    		
                    		list.add ( "localidade_latitude" );
                    		list.add ( localidade.getProperty ( "localidade_latitude" ).toString ());
                    		list.add ( "localidade_longitude" );
                    		list.add ( localidade.getProperty ( "localidade_longitude" ).toString ()) ;
                    	}
                    }
                }
            }
                        
            list.add ( "#total" );
            
            for ( Map.Entry<String, Integer> entry : listCity.entrySet ()) 
        	{
        		list.add ( "localidade_city" );
        		list.add ( entry.getKey ());
        		list.add ( "city_total" );
        		list.add ( entry.getValue ().toString ());
        	}

            list.add ( "#stats" );
            
            for ( Map.Entry<String, Integer> entry : listType.entrySet ()) 
        	{
        		list.add ( "fraude_type" );
        		list.add ( entry.getKey ());
        		list.add ( "porcentagem" );
        		list.add ( "" + ( entry.getValue () * 100 / totFraudes ) + "" );
        	}
            
        	return list;
        }
        
        public Iterable<String> getFraudesByState ( String byState, Date dtIni, Date dtFin ) 
        {
        	int totFraudes = 0 ;
        	
        	Map<String, Integer> listCity = new HashMap<String, Integer> () ;
        	Map<String, Integer> listType = new HashMap<String, Integer> () ;
        	
        	ArrayList<String> list = new ArrayList<String> () ;
        	
        	Iterable<Entity> entitiesFraude = Util.listEntities ( "Fraude", null, null ) ;

            for ( Entity entityF : entitiesFraude )
            {
            	Date dataFraude = Common.cDate ( entityF.getProperty ( "fraude_data_deteccao" ).toString ()) ;
 
                if ( dataFraude.after ( dtIni ) && dataFraude.before ( dtFin )) 
                {
                    String transId = entityF.getProperty ( "trans_id" ).toString () ;
                            
                    Iterable<Entity> entitiesTransacao = Util.listEntities ( "Transacao", "trans_id", transId ) ;
                            
                    for ( Entity entityT : entitiesTransacao ) 
                    {
                    	String localidadeId = entityT.getProperty ( "localidade_id" ).toString () ;
                   	
                    	Entity localidade = Localidade.getSingleLocalidade ( localidadeId ) ;
                    	
                    	String szState = localidade.getProperty ( "localidade_state" ).toString () ;
                    	
                    	if ( byState.equals ( szState ))
                    	{
                            String szType = entityF.getProperty ( "fraude_type" ).toString () ;
                    		
                    		if ( listType.containsKey ( szType ))
                                listType.put ( szType, listType.get ( szType ) + 1 );
                            else
                                listType.put ( szType, 1 );

                    		totFraudes += 1 ;
                    		
                    		String szCity = localidade.getProperty ( "localidade_city" ).toString () ;
                    		
                    		if ( listCity.containsKey ( szCity ))
                                listCity.put ( szCity, listCity.get ( szCity ) + 1 );
                            else
                                listCity.put ( szCity, 1 );
                    		                    		
                    		list.add ( "localidade_latitude" );
                    		list.add ( localidade.getProperty ( "localidade_latitude" ).toString ());
                    		list.add ( "localidade_longitude" );
                    		list.add ( localidade.getProperty ( "localidade_longitude" ).toString ()) ;
                    	}
                    }
                }
            }
            
            list.add ( "#total" );
            
            for ( Map.Entry<String, Integer> entry : listCity.entrySet ()) 
        	{
        		list.add ( "localidade_city" );
        		list.add ( entry.getKey ());
        		list.add ( "city_total" );
        		list.add ( entry.getValue ().toString ());
        	}
        	
            list.add ( "#stats" );
            
            for ( Map.Entry<String, Integer> entry : listType.entrySet ()) 
        	{
        		list.add ( "fraude_type" );
        		list.add ( entry.getKey ());
        		list.add ( "porcentagem" );
        		list.add ( "" + ( entry.getValue () * 100 / totFraudes ) + "" );
        	}
            
            return list;
        }
        
        public Iterable<String> getFraudesByCity ( String byCity, Date dtIni, Date dtFin ) 
        {
        	int totFraudes = 0 ;
        	
        	Map<String, Integer> listCity = new HashMap<String, Integer> () ;
        	Map<String, Integer> listType = new HashMap<String, Integer> () ;
        	
        	ArrayList<String> list = new ArrayList<String> () ;
       	
        	Iterable<Entity> entitiesFraude = Util.listEntities ( "Fraude", null, null ) ;

            for ( Entity entityF : entitiesFraude )
            {
            	Date dataFraude = Common.cDate ( entityF.getProperty ( "fraude_data_deteccao" ).toString ()) ;
 
                if ( dataFraude.after ( dtIni ) && dataFraude.before ( dtFin )) 
                {
                    String transId = entityF.getProperty ( "trans_id" ).toString () ;
                            
                    Iterable<Entity> entitiesTransacao = Util.listEntities ( "Transacao", "trans_id", transId ) ;
                            
                    for ( Entity entityT : entitiesTransacao ) 
                    {
                    	String localidadeId = entityT.getProperty ( "localidade_id" ).toString () ;
                   	
                    	Entity localidade = Localidade.getSingleLocalidade ( localidadeId ) ;
                    	
                    	String szCity = localidade.getProperty ( "localidade_city" ).toString () ;
                    	
                    	if ( byCity.equals ( szCity ))
                    	{
                            String szType = entityF.getProperty ( "fraude_type" ).toString () ;
                    		
                    		if ( listType.containsKey ( szType ))
                                listType.put ( szType, listType.get ( szType ) + 1 );
                            else
                                listType.put ( szType, 1 );
                    		
                    		totFraudes += 1 ;
                    		
                    		if ( listCity.containsKey ( szCity ))
                                listCity.put ( szCity, listCity.get ( szCity ) + 1 );
                            else
                                listCity.put ( szCity, 1 );
                    		                    		
                    		list.add ( "localidade_latitude" );
                    		list.add ( localidade.getProperty ( "localidade_latitude" ).toString ());
                    		list.add ( "localidade_longitude" );
                    		list.add ( localidade.getProperty ( "localidade_longitude" ).toString ()) ;
                    	}
                    }
                }
            }

            list.add ( "#total" );
            
            for ( Map.Entry<String, Integer> entry : listCity.entrySet ()) 
        	{
        		list.add ( "localidade_city" );
        		list.add ( entry.getKey ());
        		list.add ( "city_total" );
        		list.add ( entry.getValue ().toString ());
        	}
            
            list.add ( "#stats" );
            
            for ( Map.Entry<String, Integer> entry : listType.entrySet ()) 
        	{
        		list.add ( "fraude_type" );
        		list.add ( entry.getKey ());
        		list.add ( "porcentagem" );
        		list.add ( "" + ( entry.getValue () * 100 / totFraudes ) + "" );
        	}

            return list;
        }
        
        public String writeJSON ( Iterable<String> list ) 
        {
        	logger.log ( Level.INFO, "creating JSON format object" ) ;
        	
      	    if ( list == null )
      	    {
      		    return "{ \"data\" : [] }" ;
      	    }

      	    StringBuilder sb = new StringBuilder () ;
            
      	    int i = 0;
            
      	    sb.append ( "{ \"mapa\" : [ " ) ;
             
      	    for ( Iterator<String> element = list.iterator () ; element.hasNext () ; )
      	    {
                String key = element.next ();
      	    	
      	    	if ( key.equals ( "#total" ))
      	    	{
      	    		if ( i > 0 ) 
      	      	    {
      	                sb.deleteCharAt ( sb.lastIndexOf ( "," ));
      	            }
      	            
      	      	    sb.append ( " ], \n\"total\" : [ " ) ;
      	      	    
      	      	    key = element.next ();
      	    	}
      	    	else if ( key.equals ( "#stats" ))
      	    	{
      	    		if ( i > 0 ) 
      	      	    {
      	                sb.deleteCharAt ( sb.lastIndexOf ( "," ));
      	            }
      	            
      	      	    sb.append ( " ], \n\"estatistica\" : [ " ) ;
      	      	    
      	      	    key = element.next ();
      	    	}

                sb.append ( "{ \"" + key             + "\" : \"" + element.next () + "\", " ) ;
      	    	sb.append (   "\"" + element.next () + "\" : \"" + element.next () + "\" }, \n" ) ;

                i += 4 ;
            }
            
      	    if ( i > 0 ) 
      	    {
                sb.deleteCharAt ( sb.lastIndexOf ( "," ));
            }
            
      	    sb.append ( " ] }" ) ;
            
      	    return sb.toString ();
        }
}
